package mornd.test3;

import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator;
import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.Order;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * @author: mornd
 * @dateTime: 2023/5/6 - 17:51
 * 高级切面（@Aspect）的通知方法转换为低级切面
 */
public class Demo {

    /**
     * AnnotationAwareAspectJAutoProxyCreator  bean 后处理器
     * 一个高级切面（@Aspect）的通知方法会转换为一个低级切面
     * 作用：
     *  1、（findEligibleAdvisors）先去容器中找，目标是否匹配切面规则，返回 List<Advisor> 低级切面集合
     *  2、（wrapIfNecessary）根据第一步返回的集合创建代理增强类，如果集合为空就返回原对象
     */
    public static void main(String[] args) throws Exception {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("aspect1", Aspect1.class);
        context.registerBean("config", Config.class);

        // 解析 @Configuration @Bean 注解的 BeanFactory 后者处理器
        context.registerBean(ConfigurationClassPostProcessor.class);

        // bean 后处理器 解析 @Aspect 并产生代理
        // List<Advisor> findEligibleAdvisors(Class String) 根据目标类型找到所有与目标方法所匹配的所有低级切面（高级切面会转换为低级切面）
        // wrapIfNecessary
        context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);

        context.refresh();

        AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        Method m1 = AbstractAdvisorAutoProxyCreator.class.getDeclaredMethod("findEligibleAdvisors", Class.class, String.class);
        m1.setAccessible(true);
        List<Advisor> advisorLIst = (List<Advisor>) m1.invoke(creator, Target1.class, "target1");
        List<Advisor> advisorLIst2 = (List<Advisor>) m1.invoke(creator, Target2.class, "target2");
        /*
            size = 4
            spring自身 -> ADVISOR
            config 类中的一个低级切面
            @Spect 类中的两个，1个高级切面转为2个低级切面，分别是before，after
         */
        System.out.println("advisorLIst = " + advisorLIst);
        System.out.println("advisorLIst2 = " + advisorLIst2); // 0个

        Method m2 = AbstractAutoProxyCreator.class.getDeclaredMethod("wrapIfNecessary", Object.class, String.class, Object.class);
        m2.setAccessible(true);
        Object p1 = m2.invoke(creator, new Target1(), "target1", "target1");
        System.out.println(p1.getClass()); // Demo$Target1$$EnhancerBySpringCGLIB$$7cfbb485
        Object p2 = m2.invoke(creator, new Target2(), "target2", "target2");
        System.out.println(p2.getClass()); // Demo$Target2

        ((Target1) p1).foo();
        ((Target2) p2).bar();

        //Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
        context.close();
    }

    static class Target1 {
        public void foo() {
            System.out.println("target1 foo");
        }
    }

    static class Target2 {
        public void bar() {
            System.out.println("target1 bar");
        }
    }

    // 高级切面
    @Aspect
    @Order(1) // 数字越小，优先级越高
    static class Aspect1 {

        @Before("execution(* foo())")
        public void before() {
            System.out.println("aspect1 before...");
        }

        @After("execution(* foo())")
        public void after() {
            System.out.println("aspect1 after...");
        }
    }

    @Configuration
    static class Config {

        // 低级切面
        @Bean
        public Advisor advisor(Advice advice) {
            // 切点
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* foo())");
            DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
            // 设置执行顺序
            advisor.setOrder(2);
            return advisor;
        }

        // 通知
        @Bean
        public Advice advice() {
            return (MethodInterceptor) invocation -> {
                System.out.println("advisor before...");
                Object result = invocation.proceed();
                System.out.println("advisor after...");
                return result;
            };
        }
    }


}
